package com.ynet.middleground.contract.service.impl;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;

import org.slf4j.MDC;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.*;
import com.ynet.core.log.IfpLogger;
import com.ynet.middleground.contract.utils.Map2BeanUtil;
import com.ynet.middleground.contract.utils.ValidatorUtils;

/**
 * GenericService 描述： 泛化调用统一接口类
 *
 * @author Suremotoo
 * @create 2021-02-07 17:18
 */
public interface GenericService {

    String FILE_BASE_64_NAME = "fileBase64";

    /**
     * 接口泛化调用入口
     *
     * @param requestDto 请求参数
     * @return 返回参数
     * @throws Exception
     */
    default Map<String, Object> execute(Map<String, Object> requestDto) throws Exception {

        Map<String, Object> tempMap = (Map<String, Object>)requestDto.get("tempMap");
        Assert.notNull(tempMap, "未传入调用参数");

        String methodName = (String)tempMap.get("methodName");
        Assert.notNull(methodName, "未传入调用方法");

        Method method = ClassUtils.getMethod(getClass(), methodName, null);
        Assert.notNull(method, "远程调用方法不存在, 请确认");

        IfpLogger.info("泛化调用 Method：", methodName);

        Map<String, Object> body = (Map<String, Object>)requestDto.get("body");
        try {
            Object channel = body.get("channel");
            if (!StringUtils.isEmpty(channel)) {
                MDC.put("channel", String.valueOf(channel));
            }
            Class<?>[] parameterTypes = method.getParameterTypes();
            Object[] paramObjs = new Object[parameterTypes.length];
            Map<String, Object> resp;
            for (int i = 0; i < parameterTypes.length; i++) {
                paramObjs[i] = Map2BeanUtil.map2Bean(body, parameterTypes[i]);
                Object beanValidate = ValidatorUtils.beanValidate(paramObjs[i]);
                if (beanValidate != null) {
                    resp = (Map<String, Object>)Map2BeanUtil.objectToMap(beanValidate);
                    IfpLogger.info("泛化调用响应结果：", resp.toString());
                    Map map = JSONObject.parseObject(JSONObject.toJSONString(beanValidate), Map.class);
                    IfpLogger.info("泛化调用响应结果转 JSON ：", map.toString());
                    return map;
                }
            }

            /*
             * 修改说明：
             * 由于接口会涉及文件流 fileBase64 字符串大报文，打印日志会导致 logstash 崩掉，进一步导致当前应用 OOM
             * 故，将请求、响应中涉及的 fileBase64 参数去掉，目前该参数在请求报文中涉及的有：body、result
             * author: modify by liulx at 2022-07-07 19:57:35
             * 
             */
            JSONArray paramObjsArray = JSONArray.parseArray(JSONArray.toJSONString(paramObjs));
            if (paramObjsArray != null) {
                paramObjsArray.forEach(obj -> {
                    if ((obj instanceof JSONObject)) {
                        ((JSONObject)obj).remove(FILE_BASE_64_NAME);
                    }
                });
            }
            IfpLogger.info("泛化调用 Param remove fileBase64：", paramObjsArray.toString());
            Object invoke = method.invoke(this, paramObjs);
            // resp = (Map<String, Object>)Map2BeanUtil.objectToMap(invoke);
            // IfpLogger.info("泛化调用响应结果：", resp.toString());

            // 设置时间
            SerializeConfig serializeConfig = SerializeConfig.globalInstance;
            serializeConfig.put(LocalDateTime.class, LocalDateTimeSerializer.instance);
            serializeConfig.put(Date.class, new SimpleDateFormatSerializer(defaultPattern));

            Map map = JSONObject.parseObject(JSONObject.toJSONString(invoke), Map.class);
            Object result = map.get("result");
            if (result instanceof Map) {
                Map<Object, Object> resultMap = (Map<Object, Object>)result;
                Object removeFileBaseInfo = resultMap.remove(FILE_BASE_64_NAME);
                IfpLogger.info("泛化调用响应结果转 JSON remove fileBase64 ：", map.toString());
                resultMap.put(FILE_BASE_64_NAME, removeFileBaseInfo);
                return map;
            }
            IfpLogger.info("泛化调用响应结果转 JSON ：", map.toString());
            return map;
        } finally {
            MDC.remove("channel");
        }
    }

    /**
     * 时间格式
     */
    String defaultPattern = "yyyy-MM-dd HH:mm:ss";

    class LocalDateTimeSerializer implements ObjectSerializer {

        public static final LocalDateTimeSerializer instance = new LocalDateTimeSerializer();

        public LocalDateTimeSerializer() {}

        @Override
        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
            SerializeWriter out = serializer.out;
            if (object == null) {
                out.writeNull();
            } else {
                LocalDateTime result = (LocalDateTime)object;
                // 时间戳
                // out.writeLong(result.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
                // 字符串格式
                out.writeString(result.format(DateTimeFormatter.ofPattern(defaultPattern)));
            }
        }
    }

}
